package ynu.edu.achitectureclass.service;

import ynu.edu.achitectureclass.entity.AccessToken_Sensor;
import ynu.edu.achitectureclass.entity.Device;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import ynu.edu.achitectureclass.repository.AccessTokenRepository_sensor;
import ynu.edu.achitectureclass.repository.DeviceRepository;

import javax.transaction.Transactional;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
@Service
public abstract class BaseSensorService<T> {

    @Autowired
    protected AccessTokenRepository_sensor accessTokenRepository_sensor;

    @Autowired
    protected RestTemplate restTemplate;

    @Autowired
    protected DeviceRepository deviceRepository;

    // 获取 accessToken
    public String getAccessToken() {
        Optional<AccessToken_Sensor> latestToken = accessTokenRepository_sensor.findTopByOrderByCreatedAtDesc();
        if (latestToken.isPresent() && !isTokenExpired(latestToken.get())) {
            return latestToken.get().getToken();
        }

        Map<String, String> tokenResponse = callLoginApi();
        String newToken = tokenResponse.get("accessToken");
        long expires_in = parseExpiresIn(tokenResponse.get("expiresIn"));

        AccessToken_Sensor accessToken_sensor = new AccessToken_Sensor();
        accessToken_sensor.setToken(newToken);
        accessToken_sensor.setExpire_in(expires_in);
        accessToken_sensor.setCreatedAt(LocalDateTime.now());
        accessTokenRepository_sensor.save(accessToken_sensor);

        return newToken;
    }

    private boolean isTokenExpired(AccessToken_Sensor accessToken) {
        long expireInSeconds = accessToken.getExpire_in();
        LocalDateTime createdAt = accessToken.getCreatedAt();
        long createdAtSeconds = createdAt.atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
        long expireTimeSeconds = createdAtSeconds + expireInSeconds;
        long currentTimeSeconds = System.currentTimeMillis() / 1000;
        return currentTimeSeconds >= expireTimeSeconds;
    }

    private long parseExpiresIn(String expiresInStr) {
        try {
            if (expiresInStr == null || expiresInStr.isEmpty()) {
                throw new IllegalArgumentException("expires_in is missing or empty.");
            }
            return Long.parseLong(expiresInStr);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("Invalid format for expires_in: " + expiresInStr, e);
        }
    }

    private Map<String, String> callLoginApi() {
        String url = "https://fbox360.com/idserver/core/connect/token";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
        requestBody.add("scope", "fbox");
        requestBody.add("client_id", "68db2e8bda8d47b5b5db7eaf71c7dcdd");
        requestBody.add("client_secret", "76dc724c95004acab25482d344dab407");
        requestBody.add("grant_type", "client_credentials");

        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(requestBody, headers);

        ResponseEntity<Map> response = restTemplate.postForEntity(url, request, Map.class);

        Map<String, Object> responseBody = response.getBody();
        if (responseBody == null) {
            throw new RuntimeException("Error: Empty response body from token API.");
        }

        String accessToken = (String) responseBody.get("access_token");
        Object expiresInObj = responseBody.get("expires_in");
        String expiresInStr = null;

        if (expiresInObj instanceof String) {
            expiresInStr = (String) expiresInObj;
        } else if (expiresInObj instanceof Integer) {
            expiresInStr = String.valueOf(expiresInObj);
        } else {
            throw new RuntimeException("Error: Unexpected type for expires_in: " + expiresInObj.getClass());
        }

        if (accessToken == null || expiresInStr == null) {
            throw new RuntimeException("Error: Missing access_token or expires_in in the response.");
        }

        Map<String, String> result = new HashMap<>();
        result.put("accessToken", accessToken);
        result.put("expiresIn", expiresInStr);

        return result;
    }

    @Transactional
    public void fetchAndStoreSoilData(String sensorType) {
        String accessToken = getAccessToken();
        List<Device> devices = deviceRepository.findDevicesByType(sensorType);
        if (devices.isEmpty()) {
            throw new IllegalArgumentException("No devices found for type '" + sensorType + "'");
        }

        String boxNo = String.valueOf(devices.get(0).getGreenhouse().getId());
        List<String> sensorIds = new ArrayList<>();
        for (Device device : devices) {
            sensorIds.add(device.getSerialNumber());
        }

        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("ids", sensorIds);
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "Bearer " + accessToken);
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers);

        String url = "https://fbox360.com/api/v2/dmon/value/get?boxNo=" + boxNo;
        ResponseEntity<Object> response = restTemplate.exchange(url, HttpMethod.POST, request, Object.class);
        storeSoilData(response.getBody());
    }

    protected abstract void storeSoilData(Object data);

    public abstract T getLatestData();

    public abstract List<T> getAllData();
}